<?php class Postgres implements ifSQL {
	protected $Handle = NULL;
	protected $Stack = NULL;

	protected function Quoter (array $Matches) {
		foreach ($Matches as $Match) {
			if ('%%' === $Match) return ('%');
			if ('%L' === $Match or '%I' === $Match or '%B' === $Match or '%S' === $Match) {
				if (! count ($this->Stack)) return ('');
				$Current = array_shift ($this->Stack);
				switch ($Match) {
					case '%L': return ('"'.str_replace ('"', '""', $Current).'"');
					case '%I': return (is_numeric ($Current)) ? $Current : 0;
					case '%B': return (is_integer ($Current)) ? "B'".decbin ($Current)."'" : 0;
					case '%S': return ("'".str_replace ("'", "''", $Current)."'");
				}
			}
			return ($Match);
		}
	}

	protected function Query (array $Args) {
		if (! $this->Connected ()) return (new ErrorException ('not connected'));
		$this->Stack = $Args;
		$Query = array_shift ($this->Stack);
		$Query = preg_replace_callback ('( \\% [LIBS%] )xm', array ($this, 'Quoter'), $Query);
		pg_send_query ($this->Handle, $Query);
		$Query = pg_get_result ($this->Handle);
		switch (pg_result_status ($Query)) {
			case PGSQL_COMMAND_OK: return (pg_affected_rows ($Query));
			case PGSQL_TUPLES_OK: return (pg_fetch_all ($Query));
			case PGSQL_COPY_IN: return (TRUE);
			case PGSQL_COPY_OUT: return (TRUE);
			case PGSQL_EMPTY_QUERY: return (new ErrorException ('EMPTY QUERY'));
			case PGSQL_BAD_RESPONSE: return (new ErrorException ('BAD RESPONCE'));
			case PGSQL_NONFATAL_ERROR: return (new ErrorException ('NONFATAL ERROR'));
			case PGSQL_FATAL_ERROR: return (new ErrorException (pg_result_error ($Query)));
			default: return (new ErrorException ('UNKNOWN RESULT STATUS'));
		}
	}

	public function __construct ($ConnString) {$this->Handle or $this->Handle = pg_connect ($ConnString);}
	public function __destruct () {$this->Handle and pg_close ($this->Handle);}
	public function Connected () {return ($this->Handle and (PGSQL_CONNECTION_OK === pg_connection_status ($this->Handle))) ? TRUE : FALSE;}

	// executes a query and returns the rows affected (OR ERROREXCEPTION ON ERROR)
	public function FetchResult ($Query) {return ($this->Query (func_get_args ()));}

	// executes a query and returns the resulting table (OR ERROREXCEPTION ON ERROR)
	public function FetchTable ($Query) {return (FALSE === ($Query = $this->Query (func_get_args ()))) ? array () : $Query;}
	
	// executes a query that is supposed to return a single-cell table and returns that cell's value (OR ERROREXCEPTION ON ERROR)
	// if there is no rows in the result, returns NULL; if there are more cells in the result, they are discarded
	public function FetchValue ($Query) {
		$Query = $this->Query (func_get_args ());
		if (is_object ($Query)) return ($Query);
		if (isset ($Query [0]) and count ($Query [0])) return (reset ($Query [0]));
		return (NULL);
	}

} ?>